package com.ficus.road.maintain.service;

import com.ficus.road.maintain.core.model.*;
import com.ficus.road.maintain.dao.GpsFixTestDao;
import com.ficus.road.maintain.dao.GpsHistoryDao;
import com.ficus.road.maintain.dao.RoadInfoDao;
import com.ficus.road.maintain.dao.TheoryGpsDao;
import com.ficus.road.maintain.third.zh.roadgps.ZHBasicRoad;
import com.ficus.road.maintain.third.zh.roadgps.ZHCityAndDirectRoad;
import com.ficus.road.maintain.third.zh.roadgps.ZHTownRoad;
import com.ficus.road.maintain.util.LngLonUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author xiawen yang
 * 2022/4/7 下午5:22
 */
@Service
@Slf4j
public class TheoryGpsService {
    @Autowired
    TheoryGpsDao theoryGpsDao;
    @Autowired
    RoadInfoDao roadInfoDao;
    @Autowired
    ZHBasicRoad zhBasicRoad;
    @Autowired
    ZHCityAndDirectRoad zhCityAndDirectRoad;
    @Autowired
    GpsHistoryService gpsHistoryService;
    @Autowired
    GpsHistoryDao gpsHistoryDao;
    @Autowired
    GpsFixTestDao gpsFixTestDao;
    @Autowired
    ZHTownRoad zhTownRoad;
    @Value("${coverage.theory_gps_distance}")
    private int searchDistance;

    public void createTheoryGps(double threshold) {
        long startDelete = System.currentTimeMillis();
        theoryGpsDao.deleteAll();
        log.info("清空理论覆盖点集耗时: {}s", (System.currentTimeMillis() - startDelete) / 1000);
        // stream()用时17m
        long startCreate = System.currentTimeMillis();
        roadInfoDao.findAll().stream()
                .filter(t -> CollectionUtils.isNotEmpty(t.getCoordinates()))
                .forEach(roadInfo -> {
            AtomicInteger index = new AtomicInteger(0);
            List<Coordinate> coordinates = roadInfo.getCoordinates();
            System.out.println("采样前数据量: " + coordinates.size());
            coordinates = LngLonUtil.gpsSampling(coordinates, threshold);
            System.out.println("采样后数据量: " + coordinates.size());
            coordinates.forEach(coordinate -> {
                TheoryGps theoryGps = TheoryGps.builder()
                        .roadName(roadInfo.getRoadName())
                        .unitId(roadInfo.getUnitId())
                        .location(Arrays.asList(Double.parseDouble(coordinate.getX()), Double.parseDouble(coordinate.getY())))
                        .roadInfoId(roadInfo.getId())
                        .sequence(index.getAndIncrement())
                        .build();
                theoryGpsDao.saveOrUpdate(theoryGps);
            });
        });
        log.info("创建理论覆盖点集耗时: {}s", (System.currentTimeMillis() - startCreate) / 1000);
    }

    public void deleteAll() {
        theoryGpsDao.deleteAll();
    }

    public void refreshTheoryGps() {
        zhBasicRoad.refreshBasicRoadInfo();
        zhCityAndDirectRoad.refreshCityRoadInfo();
        zhCityAndDirectRoad.refreshDirectRoadInfo();
        zhTownRoad.refreshTownRoadInfo();
        deleteAll();
        createTheoryGps(80);
    }

    public TheoryGps geoSearch(GpsHistory gpsHistory) {
        Point point = new Point(gpsHistory.getLongitude(), gpsHistory.getLatitude());
        List<AggregationOperation> aggregationList = new ArrayList<>();
        aggregationList.add(new GeoNearDocument(new Query(), point, "distance", searchDistance));
        Aggregation agg = Aggregation.newAggregation(aggregationList);
        List<TheoryGps> result = theoryGpsDao.aggregate(agg);
        // distance排序,返回最近的
        Optional<TheoryGps> first = result.stream().min(Comparator.comparing(TheoryGps::getDistance));
        return first.orElse(null);
    }

    public List<TheoryGps> geoSearchList(GpsHistory gpsHistory) {
        Point point = new Point(gpsHistory.getLongitude(), gpsHistory.getLatitude());
        List<AggregationOperation> aggregationList = new ArrayList<>();
        aggregationList.add(new GeoNearDocument(new Query(), point, "distance", searchDistance));
        Aggregation agg = Aggregation.newAggregation(aggregationList);
        List<TheoryGps> result = theoryGpsDao.aggregate(agg);
        if (CollectionUtils.isEmpty(result)){
            return Lists.newArrayList();
        }
        if(result.size() == 1){
            return result;
        }
        return result.stream().filter(t -> t.getDistance() <= searchDistance /5).collect(Collectors.toList());
//        // distance排序,返回最近的
//        Optional<TheoryGps> first = result.stream().min(Comparator.comparing(TheoryGps::getDistance));
//        return first.orElse(null);
    }

    public List<TheoryGps> querySameRoadTheoryGpsList(TheoryGps pre, TheoryGps post) {
        Boolean reverse = pre.getSequence() > post.getSequence() ? Boolean.TRUE : Boolean.FALSE;
        int bigger = reverse ? pre.getSequence() : post.getSequence();
        int smaller = reverse ? post.getSequence() : pre.getSequence();
        Query query = new Query(Criteria.where("road_info_id").is(pre.getRoadInfoId()).and("sequence").gte(smaller).lte(bigger));
        List<TheoryGps> res = theoryGpsDao.findByQuery(query);
        if (reverse) {
            return res.stream().sorted(Comparator.comparing(TheoryGps::getSequence).reversed()).collect(Collectors.toList());
        } else {
            return res.stream().sorted(Comparator.comparing(TheoryGps::getSequence)).collect(Collectors.toList());
        }
    }

    //stream去重测试
    public void test() {
        List<RoadInfo> all = roadInfoDao.findAll();
        ArrayList<RoadInfo> collect = all.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getRoadName()))), ArrayList::new));
        System.out.println(all.size());
        System.out.println(collect.size());
    }

    public void createTestGps() {
        // 清除脏数据
        List<GpsHistory> list = gpsHistoryService.query(new Query(Criteria.where("is_fixed").is(Boolean.TRUE)));
        list.parallelStream().forEach(item -> {
            gpsHistoryDao.deleteById(item.getId());
        });

        // 添加新数据
        List<GpsHistory> all = gpsFixTestDao.findAll();
        all.parallelStream().forEach(item -> {
            item.setIsFixed(Boolean.FALSE);
            gpsHistoryDao.saveOrUpdate(item);
        });
    }

    public List<TheoryGps> findAll(){
        return theoryGpsDao.findAll();
    }
}
